package com.example.demo.configure;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import gaarason.database.autoconfiguration.MssqlAutoconfiguration;
import gaarason.database.autoconfiguration.MysqlAutoconfiguration;
import gaarason.database.bootstrap.ContainerBootstrap;
import gaarason.database.config.GaarasonDatabaseProperties;
import gaarason.database.connection.GaarasonDataSourceBuilder;
import gaarason.database.contract.connection.GaarasonDataSource;
import gaarason.database.contract.eloquent.Model;
import gaarason.database.core.Container;
import gaarason.database.eloquent.GeneralModel;
import gaarason.database.generator.GeneralGenerator;
import gaarason.database.logging.Log;
import gaarason.database.logging.LogFactory;
import gaarason.database.provider.ModelInstanceProvider;
import gaarason.database.provider.ModelShadowProvider;
import gaarason.database.spring.boot.starter.annotation.GaarasonDatabaseScanRegistrar;
import gaarason.database.spring.boot.starter.configurations.GaarasonDatabaseAutoConfiguration;
import gaarason.database.spring.boot.starter.provider.GaarasonTransactionManager;
import gaarason.database.util.ObjectUtils;
import gaarason.database.util.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;
import java.util.Collections;
import java.util.List;

/**
 * 自动配置
 *
 * @author xt
 */
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter({DruidDataSourceAutoConfigure.class, DataSourceAutoConfiguration.class})
@Import({GeneralModel.class, GeneralGenerator.class})
public class DatabaseAutoConfiguration {
    private static final Log LOGGER = LogFactory.getLog(GaarasonDatabaseAutoConfiguration.class);

    public DatabaseAutoConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean
    @ConfigurationProperties(
            prefix = "gaarason.database"
    )
    public GaarasonDatabaseProperties gaarasonDatabaseProperties() {
        return new GaarasonDatabaseProperties();
    }

    @Bean
    @ConditionalOnMissingBean
    public Container container(ApplicationContext applicationContext, GaarasonDatabaseProperties gaarasonDatabaseProperties) {
        List<String> packageOnSpringBoot = AutoConfigurationPackages.get(applicationContext);
        gaarasonDatabaseProperties.mergeScan(GaarasonDatabaseScanRegistrar.getScan()).fillPackageWhenIsEmpty(packageOnSpringBoot).fillAndVerify();
        ContainerBootstrap container = ContainerBootstrap.build(gaarasonDatabaseProperties);
        container.signUpIdentification("primary-container");
        container.defaultRegister();
        ((ModelInstanceProvider) container.getBean(ModelInstanceProvider.class)).register((modelClass) -> {
            try {
                return (Model) ObjectUtils.typeCast(applicationContext.getBean(modelClass));
            } catch (BeansException var3) {
                return (Model) ObjectUtils.typeCast(applicationContext.getBean(StringUtils.lowerFirstChar(modelClass.getSimpleName())));
            }
        });
        LOGGER.info("Model instance provider has been registered success.");
        this.bootstrapGaarasonAutoconfiguration(container);
        this.bootstrapInitialization(container);
        LOGGER.info("Container has completed initialization.");
        return container;
    }

    protected void bootstrapGaarasonAutoconfiguration(ContainerBootstrap container) {
        if (isNative()) {
            (new MysqlAutoconfiguration()).init(container);
            (new MssqlAutoconfiguration()).init(container);
        } else {
            container.bootstrapGaarasonAutoconfiguration();
        }

    }

    protected void bootstrapInitialization(ContainerBootstrap container) {
        if (isNative()) {
            ((ModelShadowProvider) container.getBean(ModelShadowProvider.class)).loadModels(Collections.singleton(GeneralModel.class));
        } else {
            container.initialization();
        }

    }

    protected static boolean isNative() {
        return "true".equals(System.getProperty("springAot")) || System.getProperty("org.graalvm.nativeimage.imagecode") != null;
    }

    @Configuration(
            proxyBeanMethods = false
    )
    public static class GaarasonDataSourceAutoconfigure {
        public GaarasonDataSourceAutoconfigure() {
        }

        @Primary
        @Bean
        @ConditionalOnMissingBean({GaarasonDataSource.class})
        public GaarasonDataSource gaarasonDataSource(DataSource dataSource, Container container) {
            LOGGER.info("GaarasonDataSource init with " + dataSource.getClass().getName());
            return GaarasonDataSourceBuilder.build(dataSource, container);
        }

        @Primary
        @Bean
        @ConditionalOnMissingBean({GaarasonTransactionManager.class})
        public GaarasonTransactionManager gaarasonTransactionManager(GaarasonDataSource gaarasonDataSource) {
            LOGGER.info("GaarasonTransactionManager init");
            return new GaarasonTransactionManager(gaarasonDataSource);
        }
    }
}
